package com.douyu.ocean.invoke;

import com.douyu.ocean.invoke.model.InvokeStatistics;
import com.douyu.ocean.invoke.model.PerformanceResult;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.javatuples.Triplet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author leiruiqi on 2018/4/17
 */

public class PerformanceCollectUtil {

    private static Logger log = LoggerFactory.getLogger(PerformanceCollectUtil.class);

    private static LinkedList<ArrayList<Triplet<String,Long,Boolean>>> bizTimesCollectList = Lists.newLinkedList();


    /**
     * 业务名，总次数，成功次数
     */
    private static Map<String,Triplet<String,Long,Long>> historyInvokeResult = Maps.newHashMap();

    private static PerfomanceAopUtil.ResultHandler resultHandler = null;

    public static void setResultHandler(PerfomanceAopUtil.ResultHandler costomResultHandler){
        if(costomResultHandler!=null){
            resultHandler = costomResultHandler;
        }

    }
    static {
        init();
    }
    public static void init(){
        bizTimesCollectList = Lists.newLinkedList();
        ArrayList<Triplet<String,Long,Boolean>> first = Lists.newArrayList();
        bizTimesCollectList.add(first);

        //initKafka();
    }

    /*public static void initKafka(){
        Properties props = new Properties();
        props.put("bootstrap.servers", "10.32.99.6:9092,10.32.99.7:9092,10.32.99.8:9092");
        props.put("serializer.class", "kafka.serializer.StringEncoder");
        props.put("request.required.acks", "1");//16M
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        Producer<String, String> producer = new KafkaProducer<String, String>(props);

        defaultProducer = producer;
    }*/




    public static void saveRecord(String key,Long millisecond,Boolean executeResult){

        bizTimesCollectList.getFirst().add(new Triplet<String, Long,Boolean>(key,millisecond,executeResult));
    }

    public static void saveRecord(String key,Long millisecond){

        bizTimesCollectList.getFirst().add(new Triplet<String, Long,Boolean>(key,millisecond,true));
    }

    public static void dataAnalysis(){

        Long current = System.currentTimeMillis();
        ArrayList<Triplet<String,Long,Boolean>> first = Lists.newArrayList();
        synchronized (bizTimesCollectList){
            bizTimesCollectList.addFirst(first);
        }

        ArrayList<Triplet<String,Long,Boolean>> calculateList = bizTimesCollectList.getLast();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Map<String,InvokeStatistics> bizRecordMap = new HashMap<String,InvokeStatistics>();

        try {
            for(Triplet<String,Long,Boolean> temp:calculateList){
                if(temp == null){
                    continue;
                }
                InvokeStatistics value = bizRecordMap.get(temp.getValue0());
                if(value == null){
                    value = new InvokeStatistics();
                    bizRecordMap.put(temp.getValue0(),value);
                }
                if(temp.getValue2()==false){
                    value.addFailCount();
                }else{
                    value.addSucceseCount();
                }
               // Double temp(Double)temp.getValue1()/1000;
                value.addCostTime(temp.getValue1());

            }
        } catch (Exception e) {
            log.error("dataAnalysis error",e);
        } finally {
            if(bizTimesCollectList.getLast()!=bizTimesCollectList.getFirst()){
                bizTimesCollectList.removeLast();
            }
        }

        List<JSONObject> batchList = Lists.newArrayList();
        JSONArray array = new JSONArray();
        List<PerformanceResult> list = Lists.newArrayList();
        for(Entry<String,InvokeStatistics> entry:bizRecordMap.entrySet()){

            LinkedList<Long> tempList = entry.getValue().getCostTimeList();
            Collections.sort(tempList,new Comparator<Long>(){
                @Override
                public int compare(Long o1, Long o2) {
                    if(o1<o2){
                        return 1;
                    }else if(o1.longValue()==o2.longValue()){
                        return 0;
                    }else{
                        return -1;
                    }
                }
            });

            //JSONObject jsono = entry2Json(entry);

           // array.add(jsono);
            //保存到历史调用结果中缓存
           // historyInvokeResult.put(entry.getKey(),new Triplet(entry.getKey(),jsono.getLongValue("allCount"),jsono.getLongValue("successCount")));
            PerformanceResult result = entry2PerformanceResult(entry);
            result.setDomain("recom_standard");
            result.setTimeStamp(current);
            list.add(result);
            historyInvokeResult.put(result.getBizName(),new Triplet(result.getBizName(),result.getAllCount(),result.getSuccCount()));

            log.info("Performance collect data:{}",JSONObject.toJSON(result));

        }

        /*JSONObject batchPerformaceInfo = new JSONObject();
        batchPerformaceInfo.put("domain","recom_standard");
        batchPerformaceInfo.put("batch_performance",array);*/

        if(resultHandler!=null){
            resultHandler.sendLastestResult(list);
        }
    }





    private static PerformanceResult entry2PerformanceResult(Entry<String,InvokeStatistics> entry){
        Long sum = 0L;
        for( Long cost:entry.getValue().getCostTimeList()){
            sum+=cost;
        }
        Long max =entry.getValue().getCostTimeList().getFirst();
        Double rangeMax = BigDecimal.valueOf((double)max/1000).setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue();
        Long min =entry.getValue().getCostTimeList().getLast();
        Double rangeMin = BigDecimal.valueOf((double)min/1000).setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue();
        Long succeseCount = entry.getValue().getSucceseCount();
        Long failCount = entry.getValue().getFailCount();
        Long allInvokeCount = succeseCount+failCount;
        Double avrage = (double)sum/allInvokeCount;
        Double rangAvrage = BigDecimal.valueOf(avrage).setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue();

        /*JSONObject jsono = new JSONObject();
        jsono.put("bizName",entry.getKey());
        jsono.put("allCount",allInvokeCount);
        jsono.put("successCount",succeseCount);
        jsono.put("failCount",failCount);
        jsono.put("avrageCost",avrage);
        jsono.put("minCost",min);
        jsono.put("maxCost",max);
*/
        PerformanceResult result = new PerformanceResult();
        result.setBizName(entry.getKey());
        result.setAllCount(allInvokeCount);
        result.setSuccCount(succeseCount);
        result.setFailCount(failCount);
        result.setAverageCost(rangAvrage);
        result.setMinCost(rangeMin);
        result.setMaxCost(rangeMax);


        return result;

    }

    public static double getInvokeRate(String name){
        Triplet<String,Long,Long> temp = historyInvokeResult.get(name);
        if(temp==null){
            return 1d;
        }else{
            double rate =  1D*temp.getValue2()/temp.getValue1();
            if(rate<0.1){
                rate = 0.1;
            }
            return rate;
        }
    }
}
